/*
 * A socket event dispatcher, features include:
 * 1. portable: worked both on Windows, MAC OS X,  LINUX platform
 * 2. a singleton pattern: only one instance of this class can exist
 */
#ifndef __EVENT_DISPATCH_H__
#define __EVENT_DISPATCH_H__

#include <list>
#include "ostype.h"
#include "util.h"

#include "Lock.h"

#if ((defined _WIN32) || (defined __APPLE__))
	enum
	{
		SOCKET_READ  = 0x1,
		SOCKET_WRITE = 0x2,
		SOCKET_EXCEP = 0x4,
		SOCKET_ALL   = 0x7
	};
#else
	constexpr uint32_t SOCKET_READ 	= 1u;				// EPOLLIN
	constexpr uint32_t SOCKET_WRITE = 4u;				// EPOLLOUT
	constexpr uint32_t SOCKET_EXCEP = 2u | 8u | 0x10u;	// EPOLLPRI | EPOLLERR | EPOLLHUP
	constexpr uint32_t SOCKET_ET 	= 1u << 31;			// EPOLLET
#endif

class CEventDispatch
{
private:
	typedef struct
	{
		callback_t 	callback;
		void 	   *user_data;
		uint64_t 	interval;
		uint64_t 	next_tick;
	} TimerItem;

private:
#ifdef _WIN32
	fd_set m_read_set;
	fd_set m_write_set;
	fd_set m_excep_set;
#elif __APPLE__
	int m_kqfd;
#else
	int m_epfd;
#endif
	CLock m_lock;
	std::list<TimerItem *> m_timer_list;
	std::list<TimerItem *> m_loop_list;

	static CEventDispatch *m_pEventDispatch;

	bool running;

public:
	/// @brief close I/O multiplexing object
	virtual ~CEventDispatch();

	/// @brief add event that are listened to the read, write, error status of the file descriptor by epoll or kqueue or other.
	/// @param fd file descriptor
	/// @param socket_event listening events type
	void AddEvent(SOCKET fd, uint32_t socket_event);
	void RemoveEvent(SOCKET fd, uint32_t socket_event);

	/// @brief add a timed task
	/// @param callback callback function
	/// @param user_data 
	/// @param interval 
	void AddTimer(callback_t callback, void *user_data, uint64_t interval);
	void RemoveTimer(callback_t callback, void *user_data);

	/// @brief add a loop task whoes interval depends on the events detection loop interval.
	/// @param callback callback function
	/// @param user_data 
	void AddLoop(callback_t callback, void *user_data);

	/// @brief wait and execute events 
	/// @param wait_timeout epoll_wait timeout
	void StartDispatch(uint32_t wait_timeout = 100);

	/// @brief stop events detection loop
	void StopDispatch();

	bool isRunning() { return running; }

	/// @brief get instance of CEventDispatch
	static CEventDispatch *Instance();

protected:
	/// @brief create I/O multiplexing object
	CEventDispatch();

private:
	/// @brief execute timeout task
	void _CheckTimer();
	void _CheckLoop();
};

#endif
